package com.girant.service;
import com.girant.DTO.SearchResultDTO;
import com.girant.entity.Material;
import com.girant.service.Result.DefineResult;
import com.girant.service.Result.KeywordValidator;
import com.girant.service.querybuilder.DefineQueryBuilder;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.ScoreSortBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

@Service
@Slf4j
public class MaterialService {
    @Autowired
    private RestHighLevelClient esClient;
    @Autowired
    private DefineQueryBuilder defineQueryBuilder;
    @Autowired
    private DefineResult defineResult;
    @Autowired
    private KeywordValidator keywordValidator;
    private static final String MATERIAL_INDEX = "probation-indext_base_material_v5";
    private static final float MIN_SCORE_THRESHOLD = 10.0f;
    private static final int MAX_PAGE_SIZE = 20;

    public SearchResultDTO<Material> search(String keyword, int size, Object[] searchAfter) throws IOException {
        // 1. 验证关键词
        keywordValidator.validate(keyword);
        BoolQueryBuilder rootQuery = defineQueryBuilder.buildQuery(keyword);

        // 2. 获取全局最高分（调用独立函数）
        float maxScore = getGlobalMaxScore(rootQuery);
        float filterMinScore = Math.max(maxScore * 0.2f, MIN_SCORE_THRESHOLD);
        log.info("全局最高分为: {}, 过滤阈值: {}", maxScore, filterMinScore);

        // 3. 第二次搜索：基于阈值过滤，获取分页数据
        int pageSize = Math.min(size, MAX_PAGE_SIZE);
        SearchSourceBuilder secondSource = new SearchSourceBuilder();
        secondSource.query(rootQuery);
        secondSource.minScore(filterMinScore); // 应用分数过滤
        secondSource.sort(new ScoreSortBuilder().order(SortOrder.DESC));
        secondSource.sort(new FieldSortBuilder("id").order(SortOrder.DESC));
        secondSource.size(pageSize);

        // 设置分页锚点
        if (searchAfter != null && searchAfter.length == 2) {
            secondSource.searchAfter(searchAfter);
        }

        SearchRequest secondRequest = new SearchRequest(MATERIAL_INDEX);
        secondRequest.source(secondSource);
        SearchResponse secondResponse = esClient.search(secondRequest, RequestOptions.DEFAULT);

        // 4. 处理结果数据（转换为List<SearchHit>以匹配process方法参数）
        SearchHit[] hitsArray = secondResponse.getHits().getHits();
        List<SearchHit> filteredHits = new ArrayList<>(Arrays.asList(hitsArray)); // 数组转List
        long totalFilteredHits = secondResponse.getHits().getTotalHits().value; // 过滤后的总条数
        Object[] nextSearchAfter = (hitsArray.length > 0) ? hitsArray[hitsArray.length - 1].getSortValues() : null;
        long tookTime = secondResponse.getTook().getMillis();

        // 5. 调用DefineResult的process方法（严格匹配参数顺序）
        // 参数说明：过滤后的结果列表、页大小、总条数、下一页锚点、耗时
        return defineResult.process(filteredHits, pageSize, totalFilteredHits, nextSearchAfter, tookTime);
    }

    /**
     * 获取全局最高分（独立函数，职责单一）
     */
    private float getGlobalMaxScore(BoolQueryBuilder query) throws IOException {
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        sourceBuilder.query(query);
        sourceBuilder.sort(new ScoreSortBuilder().order(SortOrder.DESC));
        sourceBuilder.size(1); // 仅查询1条最高分数据
        SearchRequest request = new SearchRequest(MATERIAL_INDEX);
        request.source(sourceBuilder);
        SearchResponse response = esClient.search(request, RequestOptions.DEFAULT);

        SearchHit[] hits = response.getHits().getHits();
        return (hits != null && hits.length > 0) ? hits[0].getScore() : 0;
    }
}
